package lnet

import (
	"io"
	"net"
	"strings"
	"time"
)

type Server struct {
	net.Listener
	manager      *Manager
	protocol     Protocol
	handler      Handler
	sendChanSize int
}

func NewServer(listener net.Listener, protocol Protocol, sendChanSize int, handler Handler) *Server {
	return &Server{
		Listener:     listener,
		manager:      NewManager(),
		protocol:     protocol,
		handler:      handler,
		sendChanSize: sendChanSize,
	}
}

func (server *Server) Serve() error {
	for {
		conn, err := server.Accept()
		if err != nil {
			return err
		}

		go func() {
			codec, err := server.protocol.NewCodec(conn)
			if err != nil {
				conn.Close()
				return
			}
			session := server.manager.NewSession(codec, conn, server.sendChanSize)
			server.handler.HandleSession(session)
		}()
	}
}

func (server *Server) Accept() (conn net.Conn, err error) {
	var tempDelay time.Duration
	for {
		conn, err := server.Listener.Accept()
		if err != nil {
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				time.Sleep(tempDelay)
				continue
			}
			if strings.Contains(err.Error(), "use of closed network connection") {
				return nil, io.EOF
			}
			return nil, err
		}
		return conn, nil
	}
}

func (server *Server) GetSession(sessionID uint64) *Session {
	return server.manager.GetSession(sessionID)
}

func (server *Server) Stop() {
	server.Listener.Close()
	server.manager.Close()
}
